﻿using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Linq;
using System.Text;
using System.Linq.Expressions;
using System.ComponentModel;
using System.Data.Linq.Mapping;
using System.Data.Entity;
using System.Web;
using System.Web.Mvc;
using System.Web.Routing;
using Microsoft.Practices.Unity;
using Vit.Common;
using Vit.Common.Helpers;
using Vit.Common.Extensions;
using Vit.Common.Providers;
using Vit.Web.Helpers;
using Vit.Web.Models;
using Vit.Web.Mvc.DataAnnotations;
using Vit.Web.Extensions;
using Vit.Web.Providers;

namespace Vit.Web.Providers.Data
{
    public abstract class DbContextDataProvider<TContext, TEntity> : IDataProvider<TEntity>
        where TContext : DbContext, new()
        where TEntity : class, new()
    {
        protected TContext db = Unity.Current.Resolve<TContext>();

        private ModelMetadata modelmetadata = null;
        protected ModelMetadata EntityModelMetadata
        {
            get
            {
                if (modelmetadata == null)
                    modelmetadata = ModelMetadataProviders.Current.GetMetadataForType(() => new TEntity(), typeof(TEntity));
                return modelmetadata;
            }
        }

        private IEnumerable<ModelMetadata> propertyModelmetadata = null;
        protected IEnumerable<ModelMetadata> PropertyModelMetadata
        {
            get
            {
                if (propertyModelmetadata == null)
                    propertyModelmetadata = ModelMetadataProviders.Current.GetMetadataForProperties(new TEntity(), typeof(TEntity));
                return propertyModelmetadata;
            }
        }

        protected abstract IQueryable<TEntity> OrderByDefault(IQueryable<TEntity> query);

        protected virtual bool IsCached { get { return false; } }

        protected virtual void ClearDependencyCachedData()
        {
            ICacheProvider dataCache = Unity.Current.Resolve(typeof(ICacheProvider), null) as ICacheProvider;
            if (dataCache != null)
            {
                dataCache.RemoveDependencySet(new string[] { typeof(TEntity).FullName });
            }
        }
       
        #region IDataProvider<TEntity> Members

        public void Configuration(bool enableLazyLoading, bool enableObjectTracking)
        {
            db = new TContext();
            db.Configuration(enableLazyLoading, enableObjectTracking);
        }

        public virtual IQueryable<TEntity> GetAll()
        {
            var query = db.Set<TEntity>();
            return OrderByDefault(query);
        }

        public virtual IQueryable<TEntity> Search(FormCollection conditions)
        {
            IQueryable<TEntity> query = GetAll();
            foreach (var fc in conditions.Keys)
            {
                string key = fc.ToString();
                var meta = PropertyModelMetadata.FirstOrDefault(o => o.PropertyName == key);
                if (meta != null)
                {
                    string val = conditions[key];
                    if (!string.IsNullOrWhiteSpace(val))
                    {
                        object propVal = val.ConvertTo(meta.ModelType, null);
                        if (propVal != null)
                        {
                            string condition = string.Empty;
                            if (typeof(string).IsAssignableFrom(meta.ModelType))
                                condition = string.Format("{0}.Contains(@0)", meta.PropertyName);
                            else
                                condition = string.Format("{0}==@0", meta.PropertyName);
                            query = query.Where(condition, propVal);
                        }
                    }
                }
            }
            return query;
        }

        public virtual FormCollection InitialSearchConditions()
        {
            FormCollection form = new FormCollection();
            return form;
        }

        public abstract object[] GetKeys(TEntity obj);

        public abstract TEntity GetByKeys(params object[] ids);

        public virtual TEntity New()
        {
            TEntity obj = new TEntity();
            return SetDefaultValues(obj);
        }

        public virtual TEntity SetDefaultValues(TEntity obj)
        {
            return obj;
        }

        public virtual TEntity Insert(TEntity obj)
        {
            var tbl = db.Set<TEntity>();
            tbl.Add(obj);
            db.SaveChanges();
            if (IsCached)
                ClearDependencyCachedData();
            return obj;
        }

        public virtual TEntity Modify(TEntity obj)
        {
            db.Entry<TEntity>(obj).State = System.Data.EntityState.Modified;
            db.SaveChanges();
            if (IsCached)
                ClearDependencyCachedData();
            return obj;
        }

        public virtual void Delete(TEntity obj)
        {
            var tbl = db.Set<TEntity>();
            tbl.Remove(obj);
            if (IsCached)
                ClearDependencyCachedData();
            db.SaveChanges();
        }

        #endregion

        #region IProvider Members

        public virtual string Name
        {
            get { return string.Format("DataProvider:{0}", this.GetType().FullName); }
        }

        #endregion
    }
}
